﻿using BaseFramework;
using BaseFramework.Core;
using BaseFramework.DbConnectionProvider;
using LiteFramework.Contract;
using LiteFramework.Enum;
using LiteFramework.Model;
using LiteFramework.Server.Extention;
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Security;

namespace LiteFramework.Server.Component
{
    /// <summary>
    /// 用户注册/登陆服务组件
    /// </summary>
    public class LogonComponent : AbsComponent<User>, ILogonContract
    {
        public LogonComponent(IDbConnectionProvider connectionProvider = null)
            : base(connectionProvider)
        { }

        #region Login

        /// <summary>
        /// 登陆。登陆成功将返回用户Token，返回null代表登陆不成功
        /// </summary>
        /// <param name="loginInfo"></param>
        /// <returns></returns>
        public string Login(LoginInfo loginInfo)
        {
            ServerApplication.Logger.Info("用户请求登陆：" + loginInfo.UserName);
            return Login2(loginInfo).SecurityToken;
        }

        /// <summary>
        /// 登陆。登陆成功将返回用户Token，返回null代表登陆不成功
        /// </summary>
        /// <param name="loginInfo"></param>
        /// <returns></returns>
        public LoginResult Login2(LoginInfo loginInfo)
        {
            #region 强制注销当前的登陆
            try
            {
                this.Logout();
            }
            catch { }
            finally
            {
                if (FormsAuthentication.IsEnabled) FormsAuthentication.SignOut();

                if (HttpContext.Current != null)
                {
                    if (HttpContext.Current.Session != null)
                    {
                        HttpContext.Current.Session.Clear();

                        //HttpContext.Current.Session.RemoveAll();
                        //HttpContext.Current.Session.Remove("UserName");
                        if (HttpContext.Current.Response.Cookies["UserName"] != null)
                            HttpContext.Current.Response.Cookies["UserName"].Expires = DateTime.Now.AddDays(-1);

                        HttpCookie cookie2 = new HttpCookie("ASP.NET_SessionId", "");
                        cookie2.Expires = DateTime.Now.AddYears(-1);
                        HttpContext.Current.Response.Cookies.Add(cookie2);

                        if (FormsAuthentication.IsEnabled)
                        {
                            HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
                            cookie1.Expires = DateTime.Now.AddYears(-1);
                            HttpContext.Current.Response.Cookies.Add(cookie1);
                        }

                        //HttpContext.Current.Session.Abandon();
                    }
                }

                if (FormsAuthentication.IsEnabled) FormsAuthentication.RedirectToLoginPage();
            }
            #endregion

            if (loginInfo == null)
            {
                return new LoginResult { Error = "登陆信息不完整(loginInfo=null)" };
            }
            else
            {
                loginInfo.Validate();

                User user = null;
                if (loginInfo.LoginType == LoginType.Default) // 默认验证：用户名+密码
                {
                    #region 用户名/密码登陆
                    string userName = loginInfo.UserName;
                    string password = loginInfo.Password;

                    //if (String.Compare(username, SystemResources.ADMINISTRATOR_NAME, true) == 0)
                    //{
                    //    if (Membership.FindUsersByName(username).Count == 0)
                    //    {
                    //        User user = new User(username, "admin@mail.com");
                    //        user.RealName = "系统管理员";
                    //        user.Create();
                    //    }
                    //}
                    if (string.IsNullOrEmpty(userName))
                    {
                        throw new BaseException(string.Format("未输入用户名！"));
                    }

                    string err = null;
                    if (!InternalHelper.ValidateUser(this.SqlHelper, userName, password, out err))// 将更新最后一次活动日期/时间戳
                        throw new BaseException(err);

                    using (new AdminScope())
                    {
                        user = this.GetUser(userName);
                    }
                    #endregion
                }
                else // 第三方授权
                {
                    #region 第三方授权登陆
                    IOpenAPI_Result validResult = this.validOpenId(loginInfo);
                    if (validResult.Succ)
                    {
                        UserComponent comp = ServerApplication.ComponentFactory.Create<UserComponent>();
                        using (new ApproleScope())
                        {
                            user = comp.GetUserByOpenId(validResult.LoginType, validResult.OpenId, true);
                        }

                        if (user == null) // 第三方授权用户未注册
                        {
                            //throw new BaseException(string.Format("您的{0}账号未绑定本网站账户！", EnumHelper.GetDesc(loginInfo.LoginType)));

                            // 不存在用户，自动注册
                            RegInfo regInfo = RegInfo.From(loginInfo);
                            regInfo.AutoLogin = false;

                            regInfo.Gender = validResult.Gender;
                            regInfo.NickName = validResult.NickName;
                            regInfo.HeadImgURL = validResult.HeadImgUrl;

                            RegResult regResult = this.__reg(regInfo, false);
                            if (!regResult.IsSucc)
                                throw new AccessSecurityException(string.Format("{0}登陆失败[注册未成功:{1}]！", loginInfo.LoginType, regResult.Error));

                            loginInfo.UserName = regResult.UserName;
                            //loginInfo.Password = regResult.Password;

                            user = comp.GetUser(regResult.UserName, true);
                        }
                    }
                    #endregion
                }

                //if (mUser == null)
                //    throw new BaseException(string.Format("用户不存在！"));
                if (user == null)
                    throw new BaseException(string.Format("用户名或密码不正确(0)！"));
                if (user.IsLockedOut)
                    throw new BaseException(string.Format("用户已被锁定，请联系管理员解锁！"));
                if (!user.Active)
                    throw new BaseException(string.Format("用户未启用，请联系管理员！"));

                // 请求登陆Token
                string securityToken = null;
                using (new AdminScope())
                {
                    securityToken = this.GetSecurityToken(user, loginInfo, true);
                }


                var u = ServerApplication.User;
                System.Threading.Thread.CurrentPrincipal = u;
                if (HttpContext.Current != null)
                {
                    HttpContext.Current.User = u;
                }

                if (!string.IsNullOrEmpty(securityToken) && HttpContext.Current != null && HttpContext.Current.Response != null)
                {
                    var comp = (UserComponent)ServerApplication.ComponentFactory.Create<IUserContract>();
                    comp.AddUserToCache(ServerApplication.Current.TrueUser);

                    FormsAuthentication.SetAuthCookie(user.UserName, false);

                    if (FormsAuthentication.CookieMode != HttpCookieMode.UseUri)
                    {
                        HttpCookie cookic = null;

                        // 帐套
                        cookic = HttpContext.Current.Response.Cookies[SystemResources.Cookie_Name__Login__Last_Wspace];
                        if (cookic == null)
                        {
                            cookic = new HttpCookie(SystemResources.Cookie_Name__Login__Last_Wspace);
                            HttpContext.Current.Response.Cookies.Add(cookic);
                        }
                        cookic.Path = "/";
                        cookic.Value = loginInfo.PrimeTag;
                        cookic.Expires = DateTime.Now.AddDays(30);

                        // 用户名
                        cookic = HttpContext.Current.Response.Cookies[SystemResources.Cookie_Name__Login__Last_User_Name];
                        if (cookic == null)
                        {
                            cookic = new HttpCookie(SystemResources.Cookie_Name__Login__Last_User_Name);
                            HttpContext.Current.Response.Cookies.Add(cookic);
                        }
                        cookic.Path = "/";
                        cookic.Value = loginInfo.UserName;
                        cookic.Expires = DateTime.Now.AddDays(30);

                        // 安全证书登陆
                        cookic = HttpContext.Current.Response.Cookies[SystemResources.Cookie_Name__Login__Last_Use_Security];
                        if (cookic == null)
                        {
                            cookic = new HttpCookie(SystemResources.Cookie_Name__Login__Last_Use_Security);
                            HttpContext.Current.Response.Cookies.Add(cookic);
                        }
                        cookic.Path = "/";
                        cookic.Value = loginInfo.IsSecurity.ToString();
                        cookic.Expires = DateTime.Now.AddDays(30);
                    }
                }

                return new LoginResult { UserId = ServerApplication.User.ID, SecurityToken = securityToken };
            }
        }

        /// <summary>
        /// 获取登陆Token
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="primeTag">登陆区域（帐套）</param>
        /// <returns></returns>
        internal string GetSecurityToken(string userName, string primeTag)
        {
            LoginInfo loginInfo = new LoginInfo(userName, "");
            loginInfo.PrimeTag = primeTag;

            UserComponent comp = ServerApplication.ComponentFactory.Create<UserComponent>();
            return this.GetSecurityToken(comp.GetUser(userName, true), loginInfo, false);
        }

        /// <summary>
        /// 获取登陆Token
        /// </summary>
        /// <param name="user">成员资格用户信息</param>
        /// <param name="loginInfo">登陆信息</param>
        /// <param name="addAuth">是否进行附加验证（一般在用户登陆时填true）</param>
        /// <returns></returns>
        private string GetSecurityToken(User user, LoginInfo loginInfo, bool addAuth)
        {
            if (user != null && !user.IsLockedOut)
            {
                loginInfo.PrimeTag = ServerApplication.Current.InitPrimeTag(loginInfo.PrimeTag);

                Dictionary<string, IDbConnectionProvider> d = new Dictionary<string, IDbConnectionProvider>();
                d.Add("connectionProvider", this._connectionProvider);
                UserComponent comp = (UserComponent)ServerApplication.ComponentFactory.Create<IUserContract>(d);
                try
                {
                    user = comp.Get(user.ID);
                }
                catch (Exception ex)
                {
                    throw new AccessSecurityException(string.Format("无法获取登录用户信息({0})。", user), ex);
                }

                if (user != null)
                {
                    if (!user.Active)
                        throw new BaseException("您的账户未启用，或已被停用，请联系系统管理员。");

                    if (user.ClosingDate < DateTime.Now)
                        throw new BaseException("您的账户已超过有效期，请联系系统管理员。");

                    if (string.Compare(user.UserName, "Administrator", true) != 0) // 对超级管理员不设限
                    {
                        if (!user.Validate())
                            throw new BaseException("您的用户资料异常（数据被篡改，验签失败），不允许登陆，请联系系统管理员。");

                        if (user.Organization != null)
                        {
                            if (user.Organization.ClosingDate < DateTime.Now)
                                throw new BaseException(string.Format("您的网点信息【{0}】已超过有效期，请联系系统管理员。", user.Organization.Name));

                            Organization organ = PersistentEntityMediator<Organization>.Get(this.connectionProvider, user.Organization.Id);
                            if (!organ.Validate())
                                throw new BaseException("您的网点资料异常（数据被篡改，验签失败），不允许登陆，请联系系统管理员。");

                            if (user.Organization.Company != null)
                            {
                                if (user.Organization.Company.ClosingDate < DateTime.Now)
                                    throw new BaseException(string.Format("您的公司信息【{0}】已超过有效期，请联系系统管理员。", user.Organization.Company.Name));

                                if (!user.Organization.Company.Validate())
                                    throw new BaseException("您的公司资料异常（数据被篡改，验签失败），不允许登陆，请联系系统管理员。");
                            }
                        }
                    }

                    if (!user.IsSystem)
                    {
                        //LicenseUtility licenseUtility = LicenseUtility.GetInstance();
                        //if (Membership.GetNumberOfUsersOnline() - 1 > licenseUtility.UserNumber)
                        //{
                        //    string message = null;
                        //    if (licenseUtility.UserNumber == 3)
                        //        message = "因为许可协议限制（试用版3用户），连接服务器的请求已被拒绝。\r\n请联系软件发行商购买服务许可证书。";
                        //    else
                        //        message = "已经达到许可协议允许的最多并发连接数(" + licenseUtility.UserNumber.ToString() + "个)。\r\n请联系软件发行商购买服务许可证书，获得更多的服务器连接数。";
                        //    throw new PermissionException(message);
                        //}
                    }

                    // 进行其他的验证，如智能卡，USB-KEY
                    if (!addAuth || comp.AdditionalAuthenticate(user, loginInfo.State))
                    {
                        string securityToken = user.Guid.ToString("N").ToUpper();
                        this.UpdateUserLoginInfo(loginInfo.PrimeTag, user.ID, securityToken);

                        ServerApplication.Current.SetSecurityToken(new ContextToken(securityToken, user.UserName));

                        user.LoginTimes += 1; // 累计登录次数
                        user.LastLoginType = loginInfo.LoginType;
                        //user.KeepVersion = false;
                        //user.Update();

                        try
                        {
                            ServerApplication.WriteLog("系统登陆", ServerApplication.LogType.Info, string.Format("登陆：{0} ({1})", user.RealName, user.UserName));
                        }
                        catch { }

                        return securityToken;
                    }
                }
            }

            return null;
        }

        #endregion

        #region Logout
        /// <summary>
        /// 注销登陆
        /// </summary>
        public void Logout()
        {
            if (ServerApplication.Current.TrueUser != null)
            {
                this.UpdateUserLoginInfo(null, ServerApplication.Current.TrueUser.ID, null);

                var comp = (UserComponent)ServerApplication.ComponentFactory.Create<IUserContract>();
                comp.RemoveUserFromCache(ServerApplication.Current.TrueUser);
                try
                {
                    ServerApplication.WriteLog("系统注销", ServerApplication.LogType.Info, string.Format("注销：{0} ({1})", ServerApplication.User.RealName, ServerApplication.User.UserName));
                }
                catch { }
            }

            ServerApplication.Current.SetSecurityToken(null);
            ServerApplication.Current.DisposePrimeTag();
        }
        /// <summary>
        /// 注销登陆
        /// </summary>
        /// <param name="securityToken"></param>
        public void Logout(string securityToken)
        {
            if (!String.IsNullOrEmpty(securityToken))
            {
                string ptag;
                object puk;

                this.GetUserLoginInfo(securityToken, out ptag, out puk);
                if (puk != null)
                {
                    UpdateUserLoginInfo(null, puk, null);

                    ServerApplication.Cache.IsolatedRemove(puk.ToString());

                    var comp = (PermissionComponent)ServerApplication.ComponentFactory.Create<IPermissionContract>();
                    comp.RemoveUserFromCache(ServerApplication.User);
                    //try
                    //{
                    //    ServerApplication.WriteLog("系统注销", ServerApplication.LogType.Info, string.Format("注销：{0} ({1})", ServerApplication.User.RealName, ServerApplication.User.UserName));
                    //}
                    //catch { }
                }

                ServerApplication.Current.SetSecurityToken(null);
                ServerApplication.Current.DisposePrimeTag();
            }
        }

        private void _writeLog_Logout()
        {

        }
        #endregion

        #region Reg

        /// <summary>
        /// 用户注册。
        /// <para>注册成功时，自动登陆并返回本站的登陆SecurityToken；否则返回null</para>
        /// </summary>
        /// <param name="regInfo"></param>
        /// <returns></returns>
        public RegResult Reg(RegInfo regInfo)
        {
            return this.__reg(regInfo, true);
        }

        /// <summary>
        /// 用户注册。
        /// <para>注册成功时，自动登陆并返回本站的登陆SecurityToken；否则返回null</para>
        /// </summary>
        /// <param name="regInfo"></param>
        /// <param name="needValid">是否需要重新验证第三方用户信息</param>
        /// <returns></returns>
        public RegResult __reg(RegInfo regInfo, bool needValid)
        {
            int userId = -1;
            if (regInfo == null)
                throw new ArgumentNullException("regInfo");

            // 验证OpenId的有效性
            if (needValid)
            {
                IOpenAPI_Result valid = this.validOpenId(regInfo);
                if (!valid.Succ)
                {
                    return new RegResult(regInfo.PrimeTag)
                    {
                        IsSucc = false,
                        Error = "注册失败: " + valid.Error,
                    };
                }

                regInfo.OpenId = valid.OpenId;
            }

            User user = regInfo.ToUser();

            using (new AdminScope())
            {
                // 判断客户公司是否存在
                Company company = ServerApplication.ComponentFactory.Create<ICompanyContract>().Get(Company.CustomerCode);
                if (company == null)
                {
                    return new RegResult(regInfo.PrimeTag)
                    {
                        IsSucc = false,
                        Error = "注册失败: 网站已关闭注册功能[Comp]",
                    };
                }

                // 判断客户顶级网点是否存在
                OrganizationComponent organComp = ServerApplication.ComponentFactory.Create<IOrganizationContract>() as OrganizationComponent;
                Organization rootOrgan = organComp.GetHQ(company);
                if (rootOrgan == null)
                {
                    return new RegResult(regInfo.PrimeTag)
                    {
                        IsSucc = false,
                        Error = "注册失败: 网站已关闭注册功能[Organ]",
                    };
                }

                //using (TransactionScope ts = new TransactionScope())
                {
                    try
                    {
                        // 创建客户网点
                        Organization organ = new Organization(company);
                        organ.ParentId = rootOrgan.Id;
                        organ.Name = user.UserName;
                        //organ.Category = ServerApplication.ComponentFactory.Create<IGeneralContract>().GetCategoryByCode(company, "Organization", "Station_Level_1");
                        organComp.Save(organ);

                        // 绑定客户网点
                        user.Organization = organ;

                        UserComponent userComp = ServerApplication.ComponentFactory.Create<UserComponent>();
                        RoleComponent roleComp = ServerApplication.ComponentFactory.Create<RoleComponent>();

                        Role defaultRole = roleComp.GetDefaultRole();
                        if (defaultRole != null)
                            user.Roles = new Role[] { defaultRole };

                        string err = null;
                        bool isSucc = userComp.Save(user, out err);
                        if (!isSucc)
                        {
                            return new RegResult(regInfo.PrimeTag)
                            {
                                IsSucc = false,
                                Error = "注册失败: " + err,
                            };
                        }
                        userId = user.ID;
                        ServerApplication.WriteLog("用户注册", ServerApplication.LogType.Info, string.Format("注册成功。注册类型：{0}，用户名：{1}", regInfo.LoginType, user.UserName));
                        RegResult regResult = new RegResult(regInfo.PrimeTag)
                        {
                            IsSucc = true,
                            Error = "注册成功",
                            UserId = userId,
                            UserName = user.UserName,
                            NickName = user.NickName,
                            RealName = user.RealName,
                            HeadImgURL = user.HeadImgURL,
                            SecurityToken = null,
                        };

                        if (regInfo.AutoLogin)
                        {
                            LoginInfo loginInfo = regInfo.ToLoginInfo();
                            regResult.SecurityToken = this.Login(loginInfo);
                            regInfo.PrimeTag = loginInfo.PrimeTag;
                            regResult.Error = "登陆成功";
                        }
                        //ts.VoteCommit();
                        return regResult;
                    }
                    catch (Exception ex)
                    {
                        //ts.VoteRollBack();
                        ServerApplication.WriteLog("用户注册", ServerApplication.LogType.Info, string.Format("注册失败。注册类型：{0}，用户名：{1}", regInfo.LoginType, user.UserName), ex);
                        return new RegResult(regInfo.PrimeTag)
                        {
                            IsSucc = false,
                            Error = "注册失败：" + ex.Message,
                        };
                    }
                }
            }
        }

        #endregion

        #region UserLoginInfo
        /// <summary>
        /// 获取用户登陆信息
        /// </summary>
        /// <param name="securityToken"></param>
        /// <param name="primeTag"></param>
        /// <param name="puk"></param>
        public void GetUserLoginInfo(string securityToken, out string primeTag, out object puk)
        {
            primeTag = String.Empty;
            puk = null;

            if (!String.IsNullOrEmpty(securityToken))
            {
                var param = this.SqlHelper.CreateParameter("SecurityToken", securityToken);

                string sql = "SELECT PrimeTag, UserId FROM SYS_UserLoginInfo WHERE SecurityToken = " + param.ParameterName;
                object[] objs = this.SqlHelper.ExecArray(sql, new System.Data.IDbDataParameter[] { param });
                if (objs != null && objs.Length != 0)
                {
                    primeTag = objs[0] != DBNull.Value ? objs[0].ToString() : string.Empty;
                    puk = objs[1];
                }
            }
        }
        /// <summary>
        /// 获取用户登陆信息
        /// </summary>
        /// <param name="securityToken">安全令牌</param>
        /// <param name="primeTag">登陆区域（帐套）</param>
        /// <param name="puk">关联用户</param>
        internal void GetUserLoginInfo(object puk, out string securityToken, out string primeTag)
        {
            securityToken = String.Empty;
            primeTag = String.Empty;

            if (puk != null)
            {
                object[] objs = new object[2];

                var param = this.SqlHelper.CreateParameter("UserId", puk);

                string sql = "SELECT SecurityToken, PrimeTag FROM SYS_UserLoginInfo WHERE UserId = " + param.ParameterName;
                objs = this.SqlHelper.ExecArray(sql, param);

                if (objs != null && objs.Length != 0)
                {
                    securityToken = (objs[0] != null ? (string)objs[0] : null);
                    primeTag = (objs[1] != null ? (string)objs[1] : String.Empty);
                }
            }
        }
        /// <summary>
        /// 更新用户登陆信息
        /// </summary>
        /// <param name="puk">关联用户Id</param>
        /// <param name="securityToken">安全令牌</param>
        /// <param name="primeTag">登陆区域（帐套）</param>
        private void UpdateUserLoginInfo(string primeTag, object puk, string securityToken)
        {
            if (puk != null)
            {
                DateTime lastActivityTime = DateTime.Now;

                var param = this.SqlHelper.CreateParameter("UserId", puk);
                string sql = "SELECT UserId FROM SYS_UserLoginInfo WHERE UserId = " + param.ParameterName;
                var userId = this.SqlHelper.ExecArray(sql, param);

                if (userId != null)
                {
                    var param0 = this.SqlHelper.CreateParameter("PrimeTag", primeTag);
                    var param1 = this.SqlHelper.CreateParameter("SecurityToken", securityToken);
                    var param2 = this.SqlHelper.CreateParameter("LoginTime", lastActivityTime);
                    var param3 = this.SqlHelper.CreateParameter("UserId", puk);
                    sql = string.Format("UPDATE SYS_UserLoginInfo SET PrimeTag = {0}, SecurityToken = {1}, LoginTime = {2} WHERE UserId = {3}"
                        , param0.ParameterName
                        , param1.ParameterName
                        , param2.ParameterName
                        , param3.ParameterName);

                    this.SqlHelper.ExecNonQuery(sql, param0, param1, param2, param3);
                }
                else if (securityToken != null)
                {
                    var param0 = this.SqlHelper.CreateParameter("PrimeTag", primeTag);
                    var param1 = this.SqlHelper.CreateParameter("UserId", puk);
                    var param2 = this.SqlHelper.CreateParameter("SecurityToken", securityToken);
                    var param3 = this.SqlHelper.CreateParameter("LoginTime", lastActivityTime);
                    sql = string.Format("INSERT INTO SYS_UserLoginInfo (PrimeTag, UserId, SecurityToken, LoginTime) VALUES ({0}, {1}, {2}, {3})"
                        , param0.ParameterName
                        , param1.ParameterName
                        , param2.ParameterName
                        , param3.ParameterName);
                    this.SqlHelper.ExecNonQuery(sql, param0, param1, param2, param3);
                }

                if (securityToken != null)
                {
                    var param4 = this.SqlHelper.CreateParameter("LastActivityTime", lastActivityTime);
                    var param5 = this.SqlHelper.CreateParameter("UserId", puk);
                    sql = string.Format("UPDATE SYS_USER SET LastActivityTime = {0} WHERE ID = {1}"
                      , param4.ParameterName
                      , param5.ParameterName);
                    this.SqlHelper.ExecNonQuery(sql, param4, param5);
                }
            }
        }

        /// <summary>
        /// 发送活动心跳信号。指示当前用户状态为【在线活动】，必要时可以调用此方法以避免用户长时间未操作数据导致的登陆超时问题
        /// </summary>
        public bool HeartBeat()
        {
            if (ServerApplication.Current != null && ServerApplication.Current.TrueUser != null)
            {
                ServerApplication.ComponentFactory.Create<UserComponent>().HeartBeat();
            }

            return false;
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        internal User GetUser(string userName)
        {
            var comp = new UserComponent(this._connectionProvider);
            return comp.GetUser(userName);
        }
        #endregion

        #region 第三方授权登陆

        #region Function

        /// <summary>
        /// 验证第三方授权的OpenId有效性
        /// <para>　　思路：通过appId、openId、accessToken访问第三方应用中的信息，成功返回时视为验证通过</para>
        /// </summary>
        /// <param name="regInfo"></param>
        /// <returns></returns>
        private IOpenAPI_Result validOpenId(RegInfo regInfo)
        {
            IOpenAPI_Result result = this.__ff0c81fd_validOpenId(regInfo.LoginType, regInfo.UserName, regInfo.Password, regInfo.AuthorizationCode);
            if (result.Succ) // 第三方授权，需要通过Token换取uid时，此代码是必需
            {
                regInfo.OpenId = result.OpenId;
                regInfo.AccessToken = result.AccessToken;
            }

            return result;
        }
        /// <summary>
        /// 验证第三方授权的OpenId有效性
        /// <para>　　思路：通过appId、openId、accessToken访问第三方应用中的信息，成功返回时视为验证通过</para>
        /// </summary>
        /// <param name="loginInfo"></param>
        /// <returns></returns>
        private IOpenAPI_Result validOpenId(LoginInfo loginInfo)
        {
            IOpenAPI_Result result = this.__ff0c81fd_validOpenId(loginInfo.LoginType, loginInfo.OpenId, loginInfo.AccessToken, loginInfo.AuthorizationCode);
            if (result.Succ) // 第三方授权，需要通过Token换取uid时，此代码是必需
            {
                loginInfo.OpenId = result.OpenId;
                loginInfo.AccessToken = result.AccessToken;
            }

            return result;
        }

        /// <summary>
        /// 验证第三方授权的OpenId有效性
        /// <para>　　思路：通过appId、openId、accessToken访问第三方应用中的信息，成功返回期望的信息时，视为验证通过</para>
        /// </summary>
        /// <param name="loginType">登陆类型</param>
        /// <param name="openId">用户id</param>
        /// <param name="accessToken">用户token。类别于（appToken）</param>
        /// <param name="authorizationCode">用户授权码（用此授权码换取用户登陆Token）</param>
        /// <returns></returns>
        private IOpenAPI_Result __ff0c81fd_validOpenId(LoginType loginType, string openId, string accessToken, string authorizationCode)
        {
            try
            {
                OpenAPI_ServerInfo apiInfo = OpenAPI_ServerInfo.get(loginType);
                if (loginType == LoginType.Default)
                    return new OpenAPI_ValidResult_Succ();
                else if (loginType == LoginType.QQ)
                    return (new OpenAPI_QQ()).Valid(authorizationCode);
                else if (loginType == LoginType.WeiBo_Sina)
                    return (new OpenAPI_WeiBo()).Valid(authorizationCode);
                else
                    return new OpenAPI_ValidResult_NotSupported();
            }
            catch (Exception ex)
            {
                // 写日志
                writeLog_OpenAPI(loginType, ServerApplication.LogType.Info, string.Format("【错误】: {0}", ex.Message));

                return new OpenAPI_ValidResult_Error();
            }
        }

        private static void writeLog_OpenAPI(LoginType loginType, ServerApplication.LogType logType, string message)
        {
            System.Threading.Thread thread = new System.Threading.Thread(delegate()
            {
                ServerApplication.WriteLog("OpenAPI." + loginType.ToString(), logType, message);
            });

            thread.IsBackground = true;
            thread.Start();
        }

        #endregion

        #region Model

        #region Base

        /// <summary>
        /// 第三方服务信息
        /// </summary>
        public struct OpenAPI_ServerInfo
        {
            /// <summary>
            /// 开放平台类型
            /// </summary>
            public LoginType appType { get; private set; }
            /// <summary>
            /// 应用Id。开放平台分配的id
            /// </summary>
            public string appId { get; private set; }
            /// <summary>
            /// 应用密钥。开放平台分配的密钥
            /// </summary>
            public string appSecret { get; private set; }
            /// <summary>
            /// 应用令牌/预留验证信息
            /// </summary>
            public string appToken { get; private set; }
            /// <summary>
            /// 应用消息加解密密钥
            /// </summary>
            public string appEncryptKey { get; private set; }
            /// <summary>
            /// 应用回发地址
            /// </summary>
            public string callbackUrl { get; private set; }

            private static object __lock = new object();
            private static List<OpenAPI_ServerInfo> __list = new List<OpenAPI_ServerInfo>();
            /// <summary>
            /// 从应用程序的配置文件中读取第三方API分配的授权信息
            /// <para>　　{appType}.appId:          应用Id。开放平台分配的id</para>
            /// <para>　　{appType}.appSecret:      应用密钥。开放平台分配的密钥</para>
            /// <para>　　{appType}.appToken:       应用令牌/预留验证信息</para>
            /// <para>　　{appType}.callbackUrl:    应用回发地址</para>
            /// </summary>
            /// <param name="appType"></param>
            /// <returns></returns>
            public static OpenAPI_ServerInfo get(LoginType appType)
            {
                if (__list == null)
                    __list = new List<OpenAPI_ServerInfo>();

                if (__list.Exists(i => i.appType == appType))
                    return __list.Find(i => i.appType == appType);

                lock (__lock)
                {
                    if (__list.Exists(i => i.appType == appType))
                        return __list.Find(i => i.appType == appType);

                    OpenAPI_ServerInfo info = new OpenAPI_ServerInfo();
                    info.appType = appType;
                    info.appId = ApplicationSetting.ReadAppSetting(string.Format("{0}.appId", appType.ToString().ToLower()), string.Empty);
                    info.appSecret = ApplicationSetting.ReadAppSetting(string.Format("{0}.appSecret", appType.ToString().ToLower()), string.Empty);
                    info.appToken = ApplicationSetting.ReadAppSetting(string.Format("{0}.appToken", appType.ToString().ToLower()), string.Empty);
                    info.callbackUrl = ApplicationSetting.ReadAppSetting(string.Format("{0}.callbackUrl", appType.ToString().ToLower()), string.Empty);

                    __list.Add(info);

                    return info;
                }
            }
        }
        /// <summary>
        /// 第三方接入验证协议
        /// </summary>
        public interface IOpenAPI_Auth
        {
            /// <summary>
            /// 验证authorization_code，返回验证结果，IOpenAPI_Result.Succ=true代表成功
            /// </summary>
            /// <param name="apiInfo"></param>
            /// <param name="authorization_code"></param>
            /// <returns></returns>
            IOpenAPI_Result Valid(OpenAPI_ServerInfo apiInfo, string authorization_code);
        }
        /// <summary>
        /// 第三方接入的响应结果，协议
        /// </summary>
        public interface IOpenAPI_Result
        {
            /// <summary>
            /// OpenAPI类型
            /// </summary>
            LoginType LoginType { get; }
            /// <summary>
            /// 是否成功
            /// </summary>
            bool Succ { get; }
            /// <summary>
            /// 提示信息
            /// </summary>
            string Error { get; }
            /// <summary>
            /// 用戶ID。由第三方授权分配的唯一id
            /// </summary>
            string OpenId { get; }
            /// <summary>
            /// 用戶Token。
            /// </summary>
            string AccessToken { get; }
            /// <summary>
            /// 用戶昵称
            /// </summary>
            string NickName { get; }
            /// <summary>
            /// 性别
            /// </summary>
            Gender Gender { get; }
            /// <summary>
            /// 头像
            /// </summary>
            string HeadImgUrl { get; }
            /// <summary>
            /// 大头像
            /// </summary>
            string HeadImgUrl_Big { get; }
        }
        /// <summary>
        /// 授权结果：错误
        /// </summary>
        struct OpenAPI_ValidResult_Error : IOpenAPI_Result
        {
            public OpenAPI_ValidResult_Error(LoginType loginType)
                : this()
            {
                this.LoginType = loginType;
                this.Error = "第三方授权登陆失败";
            }

            public LoginType LoginType { get; private set; }
            public bool Succ { get { return false; } }
            public string ReturnCode { get; set; }
            public string Error { get; set; }
            public string OpenId { get { return null; } }
            public string AccessToken { get { return null; } }
            public Gender Gender { get { return Gender.Male; } }
            public string NickName { get { return null; } }
            public string HeadImgUrl { get { return null; } }
            public string HeadImgUrl_Big { get { return null; } }
        }
        /// <summary>
        /// 授权结果：成功
        /// </summary>
        struct OpenAPI_ValidResult_Succ : IOpenAPI_Result
        {
            public OpenAPI_ValidResult_Succ(LoginType loginType, string userID)
                : this()
            {
                this.LoginType = loginType;
                this.OpenId = userID;
            }

            public LoginType LoginType { get; private set; }
            public bool Succ { get { return true; } }
            public string Error { get { return "ok"; } }
            public string OpenId { get; set; }
            public string AccessToken { get { return null; } }
            public Gender Gender { get { return Gender.Male; } }
            public string NickName { get { return null; } }
            public string HeadImgUrl { get { return null; } }
            public string HeadImgUrl_Big { get { return null; } }
        }
        /// <summary>
        /// 授权结果：程序未提供认证支持
        /// </summary>
        struct OpenAPI_ValidResult_NotSupported : IOpenAPI_Result
        {
            public OpenAPI_ValidResult_NotSupported(LoginType loginType)
                : this()
            {
                this.LoginType = loginType;
            }

            public LoginType LoginType { get; private set; }
            public bool Succ { get { return false; } }
            public string Error { get { return "未接入的授权方式"; } }
            public string OpenId { get { return null; } }
            public string AccessToken { get { return null; } }
            public Gender Gender { get { return Gender.Male; } }
            public string NickName { get { return null; } }
            public string HeadImgUrl { get { return null; } }
            public string HeadImgUrl_Big { get { return null; } }
        }

        /// <summary>
        /// 第三方接入的顶层基类
        /// </summary>
        public abstract class OpenAPI_Abs : IOpenAPI_Auth
        {
            /// <summary>
            /// 第三方授权类型
            /// </summary>
            public abstract LoginType LoginType { get; }
            /// <summary>
            /// 验证authorization_code，返回验证结果，IOpenAPI_Result.Succ=true代表成功
            /// </summary>
            /// <param name="apiInfo"></param>
            /// <param name="authorization_code"></param>
            /// <returns></returns>
            public abstract IOpenAPI_Result Valid(OpenAPI_ServerInfo apiInfo, string authorization_code);

            /// <summary>
            /// 验证authorization_code，返回验证结果，IOpenAPI_Result.Succ=true代表成功
            /// </summary>
            /// <param name="authorization_code"></param>
            /// <returns></returns>
            public IOpenAPI_Result Valid(string authorization_code)
            {
                return this.Valid(this.ApiInfo, authorization_code);
            }

            /// <summary>
            /// API信息
            /// </summary>
            protected OpenAPI_ServerInfo ApiInfo { get { return OpenAPI_ServerInfo.get(this.LoginType); } }
        }

        #endregion

        #region QQ

        /// <summary>
        /// QQ互联 接入
        /// </summary>
        public class OpenAPI_QQ : OpenAPI_Abs
        {
            /*
             * １.通过authorization_code请求access_token
             * ２.通过access_token获取openId
             * ３.通过access_token与openId获取用户信息
             */

            /// <summary>
            /// 授权类型
            /// </summary>
            public override LoginType LoginType { get { return LoginType.QQ; } }

            /// <summary>
            /// 验证authorization_code，返回验证结果，IOpenAPI_Result.Succ=true代表成功
            /// </summary>
            /// <param name="apiInfo"></param>
            /// <param name="authorization_code"></param>
            /// <returns></returns>
            public override IOpenAPI_Result Valid(OpenAPI_ServerInfo apiInfo, string authorization_code)
            {
                int step = 0;
                IOpenAPI_Result result = OpenAPI_Result_QQ_AccessToken.Request(ref step, apiInfo, authorization_code);
                if (result.Succ)
                {
                    string access_token = (result as OpenAPI_Result_QQ_AccessToken).access_token;
                    result = OpenAPI_Result_QQ_UserId.Request(ref step, apiInfo, access_token);
                    if (result.Succ)
                    {
                        string openid = (result as OpenAPI_Result_QQ_UserId).openid;
                        result = OpenAPI_Result_QQ_UserInfo.Request(ref step, apiInfo, openid, access_token);
                        if (result.Succ)
                            (result as OpenAPI_Result_QQ_UserInfo).AccessToken = access_token;
                    }
                }

                return result;
            }


            /// <summary>
            /// QQ互联请求结果基类：已定义错误信息字段：ret / msg
            /// </summary>
            private abstract class OpenAPI_Result_QQ_Abs : IOpenAPI_Result
            {
                #region IOpenAPI_Result 成员
                public LoginType LoginType { get { return LoginType.QQ; } }
                public bool Succ { get { return string.IsNullOrEmpty(this.ret) || this.ret == "0"; } }
                public virtual string Error { get { return this.msg; } }
                public virtual string OpenId { get { return null; } }
                public virtual string AccessToken { get; internal protected set; }
                public virtual string NickName { get { return null; } }
                public virtual Gender Gender { get { return Gender.Male; } }
                public virtual string HeadImgUrl { get { return null; } }
                public virtual string HeadImgUrl_Big { get { return null; } }
                #endregion

                #region 错误码说明
                /*
             说明：
                http://wiki.connect.qq.com/公共返回码说明
             
             错误JSON：
                { "ret":1002, "msg":"请先登录" }
             
             ret=0: 正确返回；其它: 失败。【错误码说明】详见：公共返回码说明。
            */
                #endregion

                /// <summary>
                /// 错误代码。0: 正确返回； 其它: 失败
                /// </summary>
                public string ret { get; set; }
                /// <summary>
                /// 错误消息
                /// </summary>
                public string msg { get; set; }
            }

            /// <summary>
            /// 获取QQ互联授权用户的AccessToken：通过Authorization Code获取Access Token
            /// </summary>
            private class OpenAPI_Result_QQ_AccessToken : OpenAPI_Result_QQ_Abs
            {
                #region 接口摘要
                /*
             
             【文档url】
                http://wiki.connect.qq.com/使用authorization_code请求access_token
             
             【接口url】
                PC网站：https://graph.qq.com/oauth2.0/token
                WAP网站：https://graph.z.qq.com/moc2/token
             
             【请求方式】
                Get
             
             【请求参数】
                请求字段        必选	字段说明
                grant_type	    必须	授权类型，在本步骤中，此值为“authorization_code”。
                client_id	    必须	申请QQ登录成功后，分配给网站的appid。
                client_secret	必须	申请QQ登录成功后，分配给网站的appkey。
                code	        必须	上一步返回的authorization code。
                                        如果用户成功登录并授权，则会跳转到指定的回调地址，并在URL中带上Authorization Code。
                                        例如，回调地址为www.qq.com/my.php，则跳转到：
                                        http://www.qq.com/my.php?code=520DD95263C1CFEA087******
                                        注意此code会在10分钟内过期。
                redirect_uri	必须	与上面一步中传入的redirect_uri保持一致。

              
             【返回说明】
                如果成功返回，即可在返回包中获取到Access Token。 如：
                access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
             
             【返回字段说明】
                返回字段	    字段说明
                access_token	授权令牌，Access_Token。
                expires_in	    该access token的有效期，单位为秒。
                refresh_token	在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。
 
            【错误码说明】
                接口调用有错误时，会返回code和msg字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
                PC网站接入时，错误码详细信息请参见：100000-100031：PC网站接入时的公共返回码。
                WAP网站接入时，错误码详细信息请参见：7000-7999：通过Authorization Code获取Access Token时，发生错误。
              
                callback( {"error":100020,"error_description":"code is reused error"} );
             
            */
                #endregion

                /// <summary>
                /// 授权令牌
                /// </summary>
                public string access_token { get; set; }
                /// <summary>
                /// 授权令牌的有效期
                /// </summary>
                public int expires_in { get; set; }
                /// <summary>
                /// 在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。
                /// </summary>
                public string refresh_token { get; set; }

                /// <summary>
                /// 请求数据
                /// </summary>
                /// <param name="apiInfo"></param>
                /// <param name="authorization_code"></param>
                /// <returns></returns>
                public static IOpenAPI_Result Request(ref int step, OpenAPI_ServerInfo apiInfo, string authorization_code)
                {
                    step++;

                    if (string.IsNullOrEmpty(authorization_code))
                        return new OpenAPI_ValidResult_Error { Error = "authorization_code不能为空" };

                    string url = string.Format("https://graph.qq.com/oauth2.0/token?grant_type={0}&client_id={1}&client_secret={2}&redirect_uri={3}&code={4}", "authorization_code", apiInfo.appId, apiInfo.appSecret, apiInfo.callbackUrl, authorization_code);
                    string returnStr = NetHelper.HttpGet(url, null);

                    // 写日志
                    writeLog_OpenAPI(LoginType.QQ, ServerApplication.LogType.Info, string.Format(step + ".请求 access_token。请求url：{0}{1}\t\t\t【返回结果】: {2}", url, System.Environment.NewLine, returnStr));

                    if (string.IsNullOrEmpty(returnStr)) // 请求返回空
                        return new OpenAPI_ValidResult_Error { Error = "请求 access_token 未返回值" };

                    Dictionary<string, string> dict = null;
                    returnStr = returnStr.Trim(); // 清除头、尾的空白
                    if (returnStr.StartsWith("callback( {")) // callback( {"error":100008,"error_description":"client id is illegal"} );
                    {
                        int prefixLen = "callback( {".Length;
                        int subfixLen = "} );".Length;
                        returnStr = returnStr.Substring(prefixLen, returnStr.Length - prefixLen - subfixLen);
                        dict = TextHelper.ToDictionary(returnStr, ',', ':', new char[] { '"' }, new char[] { '"' }); // 返回字符串值转为字典对象

                        string error = dict.ContainsKey("error") ? dict["error"] : null;
                        string desc = dict.ContainsKey("error_description") ? dict["error_description"] : null;

                        if (!string.IsNullOrEmpty(error)) // 不空，代表返回的是错误消息
                            return new OpenAPI_ValidResult_Error { ReturnCode = error.Trim('"'), Error = desc.Trim('"') };
                    }
                    else
                    {
                        dict = TextHelper.ToDictionary(returnStr, '&', '='); // 返回字符串值转为字典对象
                    }

                    OpenAPI_Result_QQ_AccessToken token = new OpenAPI_Result_QQ_AccessToken();

                    token.ret = dict.ContainsKey("code") ? HttpUtility.UrlDecode(dict["code"], Encoding.UTF8) : null;
                    token.msg = dict.ContainsKey("msg") ? HttpUtility.UrlDecode(dict["msg"], Encoding.UTF8) : null;

                    token.access_token = dict.ContainsKey("access_token") ? dict["access_token"] : null;
                    token.expires_in = dict.ContainsKey("expires_in") ? TextHelper.TryParse(dict["expires_in"], 0) : 0;
                    token.refresh_token = dict.ContainsKey("refresh_token") ? dict["refresh_token"] : null;

                    return token;
                }
            }

            /// <summary>
            /// 获取QQ互联授权用户的id：通过Access Token获取用户id
            /// </summary>
            private class OpenAPI_Result_QQ_UserId : OpenAPI_Result_QQ_Abs
            {
                public override string OpenId { get { return this.openid; } }

                #region 接口摘要
                /*
             【文档url】
                http://wiki.connect.qq.com/获取用户openid_oauth2-0
             
             【接口url】
                PC网站：https://graph.qq.com/oauth2.0/me
                WAP网站：https://graph.z.qq.com/moc2/me
             
             【请求方式】
                Get
             
             【请求参数】
                请求字段        必选	类型及范围	说明
                access_token	必须	在Step1中获取到的access token。
              
             【返回说明】
                PC网站接入时，获取到用户OpenID，返回包如下：
                    callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
                WAP网站接入时，返回如下字符串：
                    client_id=100222222&openid=1704************************878C
                
                ★openid是此网站上唯一对应用户身份的标识，网站可将此ID进行存储便于用户下次登录时辨识其身份，或将其与用户在网站上的原有账号进行绑定。
             
             【错误码说明】
                接口调用有错误时，会返回code和msg字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
                PC网站接入时，错误码详细信息请参见：100000-100031：PC网站接入时的公共返回码。
                WAP网站接入时，错误码详细信息请参见：9000-9999：根据Access Token获得对应用户身份的openid时，发生错误。
            */
                #endregion

                /// <summary>
                /// 用户OpenID
                /// </summary>
                public string openid { get; private set; }

                /// <summary>
                /// 请求数据
                /// </summary>
                /// <param name="apiInfo"></param>
                /// <param name="access_token"></param>
                /// <returns></returns>
                public static IOpenAPI_Result Request(ref int step, OpenAPI_ServerInfo apiInfo, string access_token)
                {
                    step++;

                    if (string.IsNullOrEmpty(access_token))
                        return new OpenAPI_ValidResult_Error { Error = "access_token不能为空" };

                    string url = string.Format("https://graph.qq.com/oauth2.0/me?access_token={0}", access_token);
                    string returnStr = NetHelper.HttpGet(url, null);

                    // 写日志
                    writeLog_OpenAPI(LoginType.QQ, ServerApplication.LogType.Info, string.Format(step + ".请求 openid。请求url：{0}{1}\t\t\t【返回结果】: {2}", url, System.Environment.NewLine, returnStr));

                    if (string.IsNullOrEmpty(returnStr)) // 请求返回空
                        return new OpenAPI_ValidResult_Error { Error = "请求 openid 未返回值" };

                    Dictionary<string, string> dict = null;
                    returnStr = returnStr.Trim(); // 清除头、尾的空白
                    if (returnStr.StartsWith("callback( {")) // callback( {"error":100008,"error_description":"client id is illegal"} );
                    {
                        int prefixLen = "callback( {".Length;
                        int subfixLen = "} );".Length;
                        returnStr = returnStr.Substring(prefixLen, returnStr.Length - prefixLen - subfixLen);
                        dict = TextHelper.ToDictionary(returnStr, ',', ':', new char[] { '"' }, new char[] { '"' }); // 返回字符串值转为字典对象

                        string error = dict.ContainsKey("error") ? dict["error"] : null;
                        string desc = dict.ContainsKey("error_description") ? dict["error_description"] : null;

                        if (!string.IsNullOrEmpty(error)) // 不空，代表返回的是错误消息
                            return new OpenAPI_ValidResult_Error { ReturnCode = error.Trim('"'), Error = desc.Trim('"') };
                    }
                    else
                    {
                        dict = TextHelper.ToDictionary(returnStr, '&', '='); // 返回字符串值转为字典对象
                    }

                    OpenAPI_Result_QQ_UserId token = new OpenAPI_Result_QQ_UserId();

                    token.ret = dict.ContainsKey("code") ? HttpUtility.UrlDecode(dict["code"], Encoding.UTF8) : null;
                    token.msg = dict.ContainsKey("msg") ? HttpUtility.UrlDecode(dict["msg"], Encoding.UTF8) : null;

                    token.openid = dict.ContainsKey("openid") ? dict["openid"] : null;

                    return token;
                }
            }

            /// <summary>
            /// 获取QQ互联授权用户的信息。
            /// 注意：
            ///     需要显式为UserID赋值
            /// </summary>
            private class OpenAPI_Result_QQ_UserInfo : OpenAPI_Result_QQ_Abs
            {
                public override string OpenId { get { return this.__openid; } }

                #region 接口摘要
                /*
             【文档url】
                http://wiki.connect.qq.com/get_user_info
             
             【接口url】
                https://graph.qq.com/user/get_user_info

             【请求参数】
                参数说明	        描述
                ret	                返回码。0: 正确返回，其它: 失败。
                msg	                如果ret<0，会有相应的错误信息提示，返回数据全部用UTF-8编码。
                nickname	        用户在QQ空间的昵称。
                figureurl	        大小为30×30像素的QQ空间头像URL。
                figureurl_1	        大小为50×50像素的QQ空间头像URL。
                figureurl_2	        大小为100×100像素的QQ空间头像URL。
                figureurl_qq_1	    大小为40×40像素的QQ头像URL。
                figureurl_qq_2	    大小为100×100像素的QQ头像URL。需要注意，不是所有的用户都拥有QQ的100x100的头像，但40x40像素则是一定会有。
                gender	            性别。 如果获取不到则默认返回"男"
                is_yellow_vip	    标识用户是否为黄钻用户（0：不是；1：是）。
                vip	                标识用户是否为黄钻用户（0：不是；1：是）
                yellow_vip_level    黄钻等级
                level	            黄钻等级
                is_yellow_year_vip	标识是否为年费黄钻用户（0：不是； 1：是）
              
             【JSON结果示例】
                { 
                    "ret":0, 
                    "msg":"", 
                    "nickname":"Peter", 
                    "figureurl":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/30", 
                    "figureurl_1":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/50", 
                    "figureurl_2":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/100", 
                    "figureurl_qq_1":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/40", 
                    "figureurl_qq_2":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/100", 
                    "gender":"男", 
                    "is_yellow_vip":"1", 
                    "vip":"1", 
                    "yellow_vip_level":"7", 
                    "level":"7", 
                    "is_yellow_year_vip":"1" 
                }
             */
                #endregion

                #region IValid
                public override string NickName { get { return this.nickname; } }
                public override Gender Gender { get { return this.gender == "女" ? Gender.Female : Gender.Male; } }
                public override string HeadImgUrl { get { return this.figureurl_qq_1; } }
                public override string HeadImgUrl_Big { get { return this.figureurl_qq_2; } }
                #endregion

                /// <summary>
                /// 用户在QQ空间的昵称。
                /// </summary>
                public string nickname { get; set; }
                /// <summary>
                /// 大小为30×30像素的QQ空间头像URL。
                /// </summary>
                public string figureurl { get; set; }
                /// <summary>
                /// 大小为50×50像素的QQ空间头像URL。
                /// </summary>
                public string figureurl_1 { get; set; }
                /// <summary>
                /// 大小为100×100像素的QQ空间头像URL。
                /// </summary>
                public string figureurl_2 { get; set; }
                /// <summary>
                /// 大小为40×40像素的QQ头像URL。
                /// </summary>
                public string figureurl_qq_1 { get; set; }
                /// <summary>
                /// 大小为100×100像素的QQ头像URL。
                /// </summary>
                public string figureurl_qq_2 { get; set; }
                /// <summary>
                /// 性别。 如果获取不到则默认返回"男"
                /// </summary>
                public string gender { get; set; }


                protected internal string __openid { get; set; }

                /// <summary>
                /// 请求数据
                /// </summary>
                /// <param name="apiInfo"></param>
                /// <param name="openid"></param>
                /// <param name="access_token"></param>
                /// <returns></returns>
                public static IOpenAPI_Result Request(ref int step, OpenAPI_ServerInfo apiInfo, string openid, string access_token)
                {
                    step++;

                    if (string.IsNullOrEmpty(access_token))
                        return new OpenAPI_ValidResult_Error { Error = "access_token不能为空" };

                    string url = string.Format("https://graph.qq.com/user/get_user_info?oauth_consumer_key={0}&openid={1}&access_token={2}", apiInfo.appId, openid, access_token);
                    string returnStr = NetHelper.HttpGet(url, null);

                    // 写日志
                    writeLog_OpenAPI(LoginType.QQ, ServerApplication.LogType.Info, string.Format(step + ".请求 user_info。请求url：{0}{1}\t\t\t【返回结果】: {2}", url, System.Environment.NewLine, returnStr));

                    if (string.IsNullOrEmpty(returnStr)) // 请求返回空
                        return new OpenAPI_ValidResult_Error { Error = "请求 user_info 未返回值" };

                    OpenAPI_Result_QQ_UserInfo result = Newtonsoft.Json.JsonConvert.DeserializeObject<OpenAPI_Result_QQ_UserInfo>(returnStr);
                    result.__openid = openid;
                    return result;
                }
            }
        }

        #endregion

        #region WeiBo

        /// <summary>
        /// 新浪微博开放平台 接入
        /// </summary>
        public class OpenAPI_WeiBo : OpenAPI_Abs
        {
            /*
             * １.通过authorization_code请求access_token与uid
             * ２.通过access_token与uid获取用户信息
             */

            /// <summary>
            /// 授权类型
            /// </summary>
            public override LoginType LoginType { get { return LoginType.WeiBo_Sina; } }

            /// <summary>
            /// 验证authorization_code，返回验证结果，IOpenAPI_Result.Succ=true代表成功
            /// </summary>
            /// <param name="apiInfo"></param>
            /// <param name="authorization_code"></param>
            /// <returns></returns>
            public override IOpenAPI_Result Valid(OpenAPI_ServerInfo apiInfo, string authorization_code)
            {
                int step = 0;
                IOpenAPI_Result result = OpenAPI_Result_WeiBo_AccessToken.Request(ref step, apiInfo, authorization_code);
                if (result.Succ)
                {
                    string access_token = (result as OpenAPI_Result_WeiBo_AccessToken).access_token;
                    string openid = (result as OpenAPI_Result_WeiBo_AccessToken).uid;

                    result = OpenAPI_Result_WeiBo_UserInfo.Request(ref step, apiInfo, openid, access_token);
                    if (result.Succ)
                        (result as OpenAPI_Result_WeiBo_UserInfo).AccessToken = access_token;
                }

                return result;
            }

            /// <summary>
            /// 新浪微博请求结果基类：已定义错误信息字段：request / error_code / error
            /// </summary>
            private abstract class OpenAPI_Result_WeiBo_Abs : IOpenAPI_Result
            {
                #region IOpenAPI_Result 成员
                public LoginType LoginType { get { return LoginType.WeiBo_Sina; } }
                public bool Succ { get { return string.IsNullOrEmpty(this.error_code); } }
                public virtual string Error { get { return this.error; } }
                public abstract string OpenId { get; }
                public virtual string AccessToken { get; internal protected set; }
                public virtual string NickName { get { return null; } }
                public virtual Gender Gender { get { return Gender.Male; } }
                public virtual string HeadImgUrl { get { return null; } }
                public virtual string HeadImgUrl_Big { get { return null; } }
                #endregion

                #region 错误信息摘要
                /*
             说明：
                http://open.weibo.com/wiki/Error_code
             
             错误JSON：
                {
                    "request" : "/statuses/home_timeline.json",
                    "error_code" : "20502",
                    "error" : "Need you follow uid."
                }
             
             error_code为空: 正确返回；其它: 失败
            */
                #endregion

                /// <summary>
                /// 请求路径
                /// </summary>
                public string request { get; set; }
                /// <summary>
                /// 错误代码
                /// </summary>
                public string error_code { get; set; }
                /// <summary>
                /// 错误消息
                /// </summary>
                public string error { get; set; }
            }

            /// <summary>
            /// 获取新浪微博授权用户的AccessToken：通过Code获取用户AccessToken
            /// </summary>
            private class OpenAPI_Result_WeiBo_AccessToken : OpenAPI_Result_WeiBo_Abs
            {
                public override string OpenId { get { return uid; } }

                #region 接口摘要
                /*
             
             【文档url】
                http://open.weibo.com/wiki/OAuth2/access_token
             
             【接口url】
                https://api.weibo.com/oauth2/access_token
             
             【请求方式】
                Post
             
             【请求参数】
                    请求字段        必选	类型及范围	说明
                    client_id       true	string	    申请应用时分配的AppKey。
                    client_secret	true	string	    申请应用时分配的AppSecret。
                    grant_type	    true	string	    请求的类型，填写authorization_code
             
                    grant_type为authorization_code时：
                    请求字段        必选    类型及范围  说明
                    code	        true	string	    调用authorize获得的code值。
                    redirect_uri	true	string	    回调地址，需需与注册应用里的回调地址一致。
              
             【JSON结果示例】
                {
                    "access_token": "ACCESS_TOKEN",
                    "expires_in": 1234,
                    "remind_in":"798114",
                    "uid":"12341234"
                }
             
             【返回字段说明】
                    返回字段	    字段类型	字段说明
                    access_token	string	    用于调用access_token，接口获取授权后的access token。
                    expires_in	    string	    access_token的生命周期，单位是秒数。
                    remind_in	    string	    access_token的生命周期（该参数即将废弃，开发者请使用expires_in）。
                    uid	            string	    当前授权用户的UID。
              
            */
                #endregion

                /// <summary>
                /// 授权令牌
                /// </summary>
                public string access_token { get; set; }
                /// <summary>
                /// 当前授权用户的UID
                /// </summary>
                public string uid { get; set; }
                /// <summary>
                /// access_token的生命周期，单位是秒数
                /// </summary>
                public int expires_in { get; set; }

                /// <summary>
                /// 请求数据
                /// </summary>
                /// <param name="apiInfo"></param>
                /// <param name="authorization_code"></param>
                /// <returns></returns>
                public static IOpenAPI_Result Request(ref int step, OpenAPI_ServerInfo apiInfo, string authorization_code)
                {
                    step++;

                    if (string.IsNullOrEmpty(authorization_code))
                        return new OpenAPI_ValidResult_Error { Error = "authorization_code不能为空" };

                    string url = string.Format("https://api.weibo.com/oauth2/access_token?client_id={0}&client_secret={1}&grant_type=authorization_code&redirect_uri={2}&code={3}", apiInfo.appId, apiInfo.appSecret, apiInfo.callbackUrl, authorization_code);
                    string returnStr = NetHelper.HttpPost(url, null); // 注意：code失效时，Post数据将引发异常

                    // 写日志
                    writeLog_OpenAPI(LoginType.WeiBo_Sina, ServerApplication.LogType.Info, string.Format(step + ".请求 access_token。请求url：{0}{1}\t\t\t【返回结果】: {2}", url, System.Environment.NewLine, returnStr));

                    if (string.IsNullOrEmpty(returnStr)) // 请求返回空
                        return new OpenAPI_ValidResult_Error { Error = "请求 access_token 未返回值" };

                    OpenAPI_Result_WeiBo_AccessToken token = Newtonsoft.Json.JsonConvert.DeserializeObject<OpenAPI_Result_WeiBo_AccessToken>(returnStr);
                    return token;
                }
            }

            /// <summary>
            /// 获取新浪微博授权用户的id：通过Token获取用户id
            /// </summary>
            private class OpenAPI_Result_WeiBo_UserId : OpenAPI_Result_WeiBo_Abs
            {
                public override string OpenId { get { return uid; } }

                #region 接口摘要
                /*
             【文档url】
                http://open.weibo.com/wiki/2/account/get_uid
             
             【接口url】
                https://api.weibo.com/2/account/get_uid.json
             
             【请求方式】
                Get
             
             【请求参数】
                请求字段        必选	类型及范围	说明
                source	        false	string      采用OAuth授权方式不需要此参数，其他授权方式为必填参数，数值为应用的AppKey。
                access_token    false	string      采用OAuth授权方式为必填参数，其他授权方式不需要此参数，OAuth授权后获得。
              
             【JSON结果示例】
                {
                    "uid":"3456676543"
                }
            */
                #endregion

                public string uid { get; set; }
            }

            /// <summary>
            /// 获取新浪微博的用户信息：通过用户id获取用户信息（通用，与授权无关）
            /// </summary>
            private class OpenAPI_Result_WeiBo_UserInfo : OpenAPI_Result_WeiBo_Abs
            {
                public override string OpenId { get { return id; } }
                public override string NickName { get { return screen_name; } }
                public override Gender Gender { get { return gender == "m" ? Gender.Male : Gender.Female; } }
                public override string HeadImgUrl { get { return profile_image_url; } }

                #region 接口摘要
                /* 
             【文档url】
                http://open.weibo.com/wiki/2/users/show
             
             【接口url】
                https://api.weibo.com/2/users/show.json
             
             【请求方式】
                Get
             
             【请求参数】
                    请求字段        必选	类型及范围	说明
                    source	        false	string	    采用OAuth授权方式不需要此参数，其他授权方式为必填参数，数值为应用的AppKey。
                    access_token	false	string	    采用OAuth授权方式为必填参数，其他授权方式不需要此参数，OAuth授权后获得。
                    uid	            false	int64	    需要查询的用户ID。
                    screen_name	    false	string	    需要查询的用户昵称。
             
                    【注: 参数uid与screen_name二者必选其一，且只能选其一】
              
             【JSON结果示例】
                {
                    "id": 1404376560,
                    "screen_name": "zaku",
                    "name": "zaku",
                    "province": "11",
                    "city": "5",
                    "location": "北京 朝阳区",
                    "description": "人生五十年，乃如梦如幻；有生斯有死，壮士复何憾。",
                    "url": "http://blog.sina.com.cn/zaku",
                    "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
                    "domain": "zaku",
                    "gender": "m",
                    "followers_count": 1204,
                    "friends_count": 447,
                    "statuses_count": 2908,
                    "favourites_count": 0,
                    "created_at": "Fri Aug 28 00:00:00 +0800 2009",
                    "following": false,
                    "allow_all_act_msg": false,
                    "geo_enabled": true,
                    "verified": false,
                    "status": {
                        "created_at": "Tue May 24 18:04:53 +0800 2011",
                        "id": 11142488790,
                        "text": "我的相机到了。",
                        "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>",
                        "favorited": false,
                        "truncated": false,
                        "in_reply_to_status_id": "",
                        "in_reply_to_user_id": "",
                        "in_reply_to_screen_name": "",
                        "geo": null,
                        "mid": "5610221544300749636",
                        "annotations": [],
                        "reposts_count": 5,
                        "comments_count": 8
                        },
                    "allow_all_comment": true,
                    "avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1",
                    "verified_reason": "",
                    "follow_me": false,
                    "online_status": 0,
                    "bi_followers_count": 215
                }

                【返回字段说明】
                    返回字段	        字段类型	字段说明
                    id	                int64	    用户UID
                    idstr	            string	    字符串型的用户UID
                    screen_name	        string	    用户昵称
                    name	            string	    友好显示名称
                    province	        int	        用户所在省级ID
                    city	            int	        用户所在城市ID
                    location	        string	    用户所在地
                    description	        string	    用户个人描述
                    url	                string	    用户博客地址
                    profile_image_url	string	    用户头像地址（中图），50×50像素
                    profile_url	        string	    用户的微博统一URL地址
                    domain	            string	    用户的个性化域名
                    weihao	            string	    用户的微号
                    gender	            string	    性别，m：男、f：女、n：未知
                    followers_count	    int	        粉丝数
                    friends_count	    int	        关注数
                    statuses_count	    int	        微博数
                    favourites_count	int	        收藏数
                    created_at	        string	    用户创建（注册）时间
                    following	        boolean	    暂未支持
                    allow_all_act_msg	boolean	    是否允许所有人给我发私信，true：是，false：否
                    geo_enabled	        boolean	    是否允许标识用户的地理位置，true：是，false：否
                    verified	        boolean	    是否是微博认证用户，即加V用户，true：是，false：否
                    verified_type	    int	        暂未支持
                    remark	            string	    用户备注信息，只有在查询用户关系时才返回此字段
                    status	            object	    用户的最近一条微博信息字段 详细
                    allow_all_comment	boolean	    是否允许所有人对我的微博进行评论，true：是，false：否
                    avatar_large	    string	    用户头像地址（大图），180×180像素
                    avatar_hd	        string	    用户头像地址（高清），高清头像原图
                    verified_reason	    string	    认证原因
                    follow_me	        boolean	    该用户是否关注当前登录用户，true：是，false：否
                    online_status	    int	        用户的在线状态，0：不在线、1：在线
                    bi_followers_count	int	        用户的互粉数
                    lang	            string	    用户当前的语言版本，zh-cn：简体中文，zh-tw：繁体中文，en：英语
            */
                #endregion

                public string id { get; set; }
                public string idstr { get; set; }
                /// <summary>
                /// 性别，m：男、f：女、n：未知
                /// </summary>
                public string gender { get; set; }
                /// <summary>
                /// 昵称
                /// </summary>
                public string screen_name { get; set; }
                /// <summary>
                /// 用户头像地址（中图），50×50像素
                /// </summary>
                public string profile_image_url { get; set; }

                /// <summary>
                /// 请求数据
                /// </summary>
                /// <param name="apiInfo"></param>
                /// <param name="openid"></param>
                /// <param name="access_token"></param>
                /// <returns></returns>
                public static IOpenAPI_Result Request(ref int step, OpenAPI_ServerInfo apiInfo, string openid, string access_token)
                {
                    step++;

                    if (string.IsNullOrEmpty(access_token))
                        return new OpenAPI_ValidResult_Error { Error = "access_token不能为空" };

                    string url = string.Format("https://api.weibo.com/2/users/show.json?uid={1}&access_token={2}", apiInfo.appId, openid, access_token);
                    string returnStr = NetHelper.HttpGet(url, null);

                    // 写日志
                    writeLog_OpenAPI(LoginType.WeiBo_Sina, ServerApplication.LogType.Info, string.Format(step + ".请求 user_info。请求url：{0}{1}\t\t\t【返回结果】: {2}", url, System.Environment.NewLine, returnStr));

                    if (string.IsNullOrEmpty(returnStr)) // 请求返回空
                        return new OpenAPI_ValidResult_Error { Error = "请求 user_info 未返回值" };

                    OpenAPI_Result_WeiBo_UserInfo result = Newtonsoft.Json.JsonConvert.DeserializeObject<OpenAPI_Result_WeiBo_UserInfo>(returnStr);
                    return result;
                }
            }
        }

        #endregion

        #endregion

        #endregion
    }
}